/**
 * @file   FormNode.java
 * @author Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Wed Aug  8 03:32:01 2007
 * 
 * @brief  Nodo de un formulario
 * 
 * 
 */
package sim.forms;

import java.util.*;

import sim.framework.*;
import sim.utils.Utils;
import sim.utils.parser.*;
import sim.framework.gui.*;
import sim.framework.dialog.*;

public final class FrmNode implements ParserScan, FrmCalc {

    public static final String STR_TOKEN = "_";
    public static final byte[] TOKEN = STR_TOKEN.getBytes();

    private static final int TOKEN_NODE = Utils.TOKEN_PIPE;

    private static final byte[] WORD = {'d', 'l', 'g'};

    // Campos de una linea que definen un nodo.
    private static final int HIT_WORD = 1; // Tag que identifica a una linea con información de un nodo.
    private static final int HIT_NODE = 2; // Identificador del nodo
    private static final int HIT_TYPE = 3; // Tipo de nodo (Tipo de control para ingresar respuesta)
    private static final int HIT_SEEK = 4; // Etiqueta que muestra la pregunta.
    private static final int HIT_OPER = 5; // Operador para evaluar respuesta.
    private static final int HIT_INFO = 6; // Información adicional a la pregunta.
    private static final int HIT_FILE = 7; // Archivo maestro para llenar el control segun tipo.

    // Idenfificadores de los operadores de comparación.
    private static final int OPER_NNEXT = 0; // Siguiente nodo.
    private static final int OPER_NEXIT = 1; // Último nodo.
    private static final int OPER_EQUAL = 2; // Operador igual
    private static final int OPER_MAJOR = 3; // Operador mayor que
    private static final int OPER_MINOR = 4; // Operador menor que
    private static final int OPER_MAJEQ = 5; // Operador igual que
    private static final int OPER_MINEQ = 6; // Operador menor que

    // Identificadores nemotécnicos para escribir archivos de tipo Forms v1.0.
    private static final String OPER_STR_V1_NNEXT = "next";
    private static final String OPER_STR_V1_NEXIT = "end";
    private static final String OPER_STR_V1_EQUAL = "equal";
    private static final String OPER_STR_V1_MAJOR = "major";
    private static final String OPER_STR_V1_MINOR = "minor";
    private static final String OPER_STR_V1_MAJEQ = "majeq";
    private static final String OPER_STR_V1_MINEQ = "mineq";
    // Identificadores nemotécnicos para escribir archivos de tipo Forms v2.0.
    private static final String OPER_STR_NNEXT = "->";
    private static final String OPER_STR_NEXIT = "><";
    private static final String OPER_STR_EQUAL = "==";
    private static final String OPER_STR_MAJOR = ">";
    private static final String OPER_STR_MINOR = "<";
    private static final String OPER_STR_MAJEQ = ">=";
    private static final String OPER_STR_MINEQ = "<=";

    protected static final int NODE_NEXT = -1;
    protected static final int NODE_EXIT = -2;

    // Indica para como se utiliza el parse.
    private static final int PARSER_FOR_CREATE = 0;
    private static final int PARSER_FOR_SETOPT = 1;
    //private static final int PARSER_FOR_PUTOPT = 2;

    private static final byte ITEMS_INIT_SIZE =  8;
    private static final byte VALUE_INIT_SIZE = 32;

    // Control
    private String m_Encoding = null;
    private int m_ParserFor;
    private boolean m_Put = false;
    protected Vector m_Items = null;	// Opciones disponibles
    protected StringBuffer m_Value = new StringBuffer(FrmNode.VALUE_INIT_SIZE); // Valor ingresado al nodo.

    // Valores del nodo
    protected int m_Node;	// Identificador del nodo (Obligatorio).
    protected int m_Type;	// Typo de nodo, determina el tipo de control. (Obligatorio).
    protected String m_Seek = null;	// Etiqueta mostrada sobre el control (Opcional).
    protected int m_Oper = FrmNode.OPER_NNEXT;	// Comparador de valor ingresado (Opcional).
    protected String m_Info = null;    // Información general (Opcional).
    protected String m_File = null;    // Nombre de una archivo de tipo lista (Opcional).
    
    private FrmNode(final String encoding) {
	m_Encoding = encoding;
    }

    protected static FrmNode create(final String encoding, final byte[] line, int start, int length) {
	FrmNode f;
	
	f = new FrmNode(encoding);
	Parser p = new Parser(f, Utils.TOKEN, TOKEN_NODE, 1, Parser.FORWARD);	
	f.m_ParserFor = FrmNode.PARSER_FOR_CREATE;
	if (!p.scan(line, start, length)) {
	    f = null;
	}
	
	return f;
    }

    /** 
     * Devuelve el identificador del nodo.
     * 
     * 
     * @return Devuelve un valor entero mayor igual que cero.
     */
    public final int getId() {
	return m_Node;
    }

    /** 
     * Devuelve la pregunta asociada al nodo.
     * 
     * @return Devuelve una cadena de caracteres.
     */
    public final String getSeek() {
	return m_Seek;
    }

    /** 
     * Devuelve el Tipo del control
     * 
     * @return Devuelve un valor entre GuiCtrl::UNKNOW y GuiCtrl::RADIOGROUP
     *         incluyendo.
     */
    public final int getType() {
	return m_Type;
    }

    /** 
     * Devuelve le nombre del archivo maestro de items.
     * 
     * @return Devuelve una cadena de caracteres con el nombre de archivo
     *         maestro a partir del cual se cargan los items.
     */
    public String getFilename() {
	return m_File;
    }

    /** 
     * Devuelve el número de items asociados a este nodo.
     * 
     * 
     * @return Devuelve un valor entero mayor igual a cero.
     */
    public int itemsCount() {
	int ret = 0;
	if (m_Items != null) {
	    ret = m_Items.size();
	}
	return ret;
    }

    private static int str2eval(byte[] line, int start, int length, String encoding) {
	String s;

	// Lo siguiente no me gusta, deberia hacerse una comaparación
	// a nivel de bytes.
	s = Utils.decoding(line, start, length, encoding);

	// Version 1.0
	if (s.compareTo(OPER_STR_V1_NNEXT) == 0)
	    return FrmNode.OPER_NNEXT;
	if (s.compareTo(OPER_STR_V1_NEXIT) == 0)
	    return FrmNode.OPER_NEXIT;
	if (s.compareTo(OPER_STR_V1_EQUAL) == 0)
	    return FrmNode.OPER_EQUAL;
	if (s.compareTo(OPER_STR_V1_MAJOR) == 0)
	    return FrmNode.OPER_MAJOR;
	if (s.compareTo(OPER_STR_V1_MINOR) == 0)
	    return FrmNode.OPER_MINOR;
	if (s.compareTo(OPER_STR_V1_MAJEQ) == 0)
	    return FrmNode.OPER_MAJEQ;
	if (s.compareTo(OPER_STR_V1_MINEQ) == 0)
	    return FrmNode.OPER_MINEQ;

	// Version 2.0
	if (s.compareTo(OPER_STR_NNEXT) == 0)
	    return FrmNode.OPER_NNEXT;
	if (s.compareTo(OPER_STR_NEXIT) == 0)
	    return FrmNode.OPER_NEXIT;
	if (s.compareTo(OPER_STR_EQUAL) == 0)
	    return FrmNode.OPER_EQUAL;
	if (s.compareTo(OPER_STR_MAJOR) == 0)
	    return FrmNode.OPER_MAJOR;
	if (s.compareTo(OPER_STR_MINOR) == 0)
	    return FrmNode.OPER_MINOR;
	if (s.compareTo(OPER_STR_MAJEQ) == 0)
	    return FrmNode.OPER_MAJEQ;
	if (s.compareTo(OPER_STR_MINEQ) == 0)
	    return FrmNode.OPER_MINEQ;

	// Si no se entiende el tipo de operador, por defecto vale FrmNode.OPER_NNEXT.
	return FrmNode.OPER_NNEXT;
    }

    public boolean parserHit(int hit, final byte[] line, int start, int length, Object something) {
	boolean ret;
	ret = false;
	switch (m_ParserFor) {
	case FrmNode.PARSER_FOR_CREATE:
	    ret = parserHitCreate(hit, line, start, length);
	    break;
	case FrmNode.PARSER_FOR_SETOPT:
	    ret = parserHitSetOpt(hit, line, start, length);
	    break;
	}
	return ret;
    }

    private boolean parserHitCreate(int hit, final byte[] line, int start, int length) {
	boolean ret;
	ret = true;

	//#ifdef DEBUG_FRMNODE
	//# System.out.println("FrmNode::parserHitCreate() ENTERED");
	//#endif

	switch (hit) {
	case FrmNode.HIT_WORD:
	    ret = (Utils.memncmp(line, start, FrmNode.WORD, 0, FrmNode.WORD.length) == 0);
	    //#ifdef DEBUG_FRMNODE
	    //# System.out.println("\tcase FrmNode.HIT_WORD dice " + (ret ? "TRUE" : "FALSE"));
	    //#endif
	    break;
	case FrmNode.HIT_NODE:
	    m_Node = Utils.memtoul(line, start, length, 10);
	    break;
	case FrmNode.HIT_TYPE:
 	    m_Type = GuiCtrl.str2type(line, start, length);
	    m_Type = (m_Type == GuiCtrl.UNKNOW) ? GuiCtrl.TEXT : m_Type;
	    break;
	case FrmNode.HIT_SEEK:
	    if (length > 0) {
		m_Seek = Utils.decoding(line, start, length, m_Encoding);
	    }
	    break;
	case FrmNode.HIT_OPER:
	    if (length > 0) {		
		m_Oper = FrmNode.str2eval(line, start, length, m_Encoding);
	    }
	    break;
	case FrmNode.HIT_INFO:
	    if (length > 0) {
		m_Info = Utils.decoding(line, start, length, m_Encoding);
	    }
	    break;
	case FrmNode.HIT_FILE:
	    if (length > 0) { 
		m_File = Utils.decoding(line, start, length, m_Encoding);
	    }
	    break;
	}

	//#ifdef DEBUG_FRMNODE
	//# System.out.println("FrmNode::parserHitCreate() EXITING");
	//#endif

	return ret;
    }

    private boolean parserHitSetOpt(int hit, final byte[] line, int start, int length) {
	String option;
	FrmItem item;
	
	option = Utils.decoding(line, start, length, m_Encoding);
	
	item = selectItemByOption(option);
	if (item != null) {
	    item.m_Selected = true;
	}
	
	return true;
    }
    
    public boolean parserEnd(boolean cancel) {
	// Aqui se debe validar si uno o más campos estraidos en parserHit() son
	// son incompatibles, no se complementan, o entran en conflicto. De
	// momento no hay otra validación.
	boolean ret;
	ret = true;
	switch (m_ParserFor) {
	case FrmNode.PARSER_FOR_CREATE:
	    ret = !cancel;
	    break;
	}
	return ret;
    }

    protected void appendItem(FrmItem item) {
	// Si no hay una tabla de items creada ...
	if (m_Items == null) {    
	    // ... crear una tabla.
	    m_Items = new Vector(FrmNode.ITEMS_INIT_SIZE);
	}
	// Añadir un nuevo item a la tabla.
	if (m_Items != null) {
	    item.m_Id = m_Items.size() + 1;
	    m_Items.addElement(item);
	}
    }
    
    private FrmItem selectItemById(int id) {
	Enumeration e;
	FrmItem item;
	e = m_Items.elements();
	while(e.hasMoreElements()) {
	    item = (FrmItem)e.nextElement();
	    if (item.m_Id == id) {
		return item;
	    }
	}
	return null;
    }

    private FrmItem selectItemByOption(final String option) {
	Enumeration e;
	FrmItem item = null;
	if (m_Items != null) {
	    e = m_Items.elements();
	    while (e.hasMoreElements()) {
		item = (FrmItem)e.nextElement();
		if (option.compareTo(item.getOption()) == 0) {
		    break;
		}
	    }
	}
	return item;
    }
    
    private FrmItem selectItemByNodeValue() {
	Enumeration e;
	FrmItem item;
	// Si tiene items ...
	if (m_Items != null) {
	    e = m_Items.elements();
	    while(e.hasMoreElements()) {
		item = (FrmItem)e.nextElement();
		// ... y se parece a la entrada de usuario ...
		if (item.m_Desc.compareTo(m_Value.toString()) == 0) {
		    // Eureka!!!, devolver el código o la descripción.
		    return item;
		}
	    }
	}
	return null;
    }
    
    private void putValue() {
	Parser p;
	FrmItem item;

	if (!m_Put) {

	    m_Put = true;

	    switch (m_Type) {
	    case GuiCtrl.MULTIOPT:
		// Tipo de control selección múltiple, por ello
		// se debe buscar dentro del option, más de un
		// item seleccionado.
		p = new Parser(this, FrmItem.TOKEN, Parser.FORWARD);
		m_ParserFor = FrmNode.PARSER_FOR_SETOPT;
		p.scan(Utils.encoding(m_Value.toString(), m_Encoding));
		m_Value.setLength(0);
		break;
	    case GuiCtrl.LIST:
	    case GuiCtrl.COMBO:
		// Tipos de control selección simple, por ello
		// solo se debe buscar el item que corresponde
		// a la opción.
		item = selectItemByOption(m_Value.toString());
		if (item != null) {
		    item.m_Selected = true;
		}
		m_Value.setLength(0);
		break;
	    default:
		// Para controles del tipo entrada de usuario,
		// el valor establecido, puede o no ser opción
		item = selectItemByOption(m_Value.toString());
		if (item != null) {
		    m_Value.setLength(0);
		    m_Value.append(item.m_Desc);
		}
	    }
	}
    }
    
    protected int gotoNode(FrmEval eval) {
	int ret;

	eval.setFrmCalc(this);

	ret = FrmNode.NODE_NEXT;

	switch(m_Oper) {
	case FrmNode.OPER_NEXIT:
	    ret = FrmNode.NODE_EXIT;
	    break;
	case FrmNode.OPER_NNEXT:
	    ret = FrmNode.NODE_NEXT;
	    break;
	case FrmNode.OPER_EQUAL:
	    ret = eval.equal(this);
	    break;
	case FrmNode.OPER_MAJOR:
	    ret = eval.major(this);
	    break;
	case FrmNode.OPER_MINOR:
	    ret = eval.minor(this);
	    break;
	case FrmNode.OPER_MAJEQ:
	    ret = eval.majorORequal(this);
	    break;
	case FrmNode.OPER_MINEQ:
	    ret = eval.minorORequal(this);
	    break;
	}

	return ret;
    }

    private void cleanNode() {
	FrmItem item;
	Enumeration e;

	// Marcar los item de este nodo como "NO" seleccionados
	if (m_Items != null) {
	    e = m_Items.elements();
	    while(e.hasMoreElements()) {
		item = (FrmItem)e.nextElement();
		item.m_Selected = false;
	    }
	}

	// Borrar el valor contenido por el control
	m_Value.setLength(0);
    }

    public void setOption(final String line) {
	m_Value.setLength(0);
	m_Value.append(line);
	m_Put = false;
    }
    
    public void setOption(final byte[] line, int start, int length) {
	setOption(Utils.decoding(line, start, length, m_Encoding));
    }
	
    public void setOption(final byte[] line) {
	setOption(line, 0, line.length);
    }
    
    /** 
     * Llamada por ModForm para recupear la opción establecida para un nodo.
     * 
     * @return Devuelve una cadena que representa la opción o opciones 
     *         establecidas para el presente node.
     */
    public String getOption() {
	Enumeration e;
	FrmItem item;
	String ret;

	ret = null;

	switch (m_Type) {
	case GuiCtrl.LIST:
	case GuiCtrl.COMBO:
	case GuiCtrl.MULTIOPT:
	    // Control de tipo selección, por ello añadimos
	    // cada option separada por token
	    // Utilizar m_Value para preparar la salida.
	    m_Value.setLength(0);
	    if (m_Items != null) {
		// Se< copia las opciones a m_Value.
		e = m_Items.elements();
		while(e.hasMoreElements()) {
		    item = (FrmItem)e.nextElement();
		    if (item.m_Selected) {
			// Si ya se añadio por lo menos una opción ...
			if (m_Value.length() > 0) {
			    // ... añadir el token de item.
			    m_Value.append(FrmItem.STR_TOKEN);
			}
			m_Value.append(item.getOption());
		    }
		}
	    }
	    ret = m_Value.toString();
	    break;
	default:
	    // Control de tipo entrada de usuario, si esta
	    // coincide con una item, devolver la opcion
	    // que representa.
	    item = selectItemByNodeValue();
	    if (item != null) {
		ret = item.getOption();
	    }
	    else {
		ret = m_Value.toString();
	    }
	}

	return ret;
    }

    protected void getOption(StringBuffer strbuffer) {
	strbuffer.append(getOption());
    }

    /** 
     * Intercambio de datos entre el nodo y el control principal de DlgForm.
     * 
     * @param dlg Dialgo asociado al nodo.
     * @param updated Si vale true, indica que los datos del control estan
     *                actualizados; en caso contrario, los datos del nodo
     *                están actualizados.
     * @param p Parser para el control GuiCtrl.MULTIOPT
     */
    protected void updateDialog(DlgForm dlg, boolean updated) {
	String string;
	int id;
	Enumeration e;
	FrmItem item;

	//#ifdef DEBUG_FRMNODE
	//# System.out.println("FrmNode::updatedDialog() ENTERED");
	//#endif

	//#ifdef DEBUG_FRMNODE
	//# if(updated)
	//#  System.out.println("\tCTRL->NODE");
	//# else
	//#  System.out.println("\tNODE->CTRL");
	//#endif

	switch (m_Type) {
	case GuiCtrl.LIST:
	case GuiCtrl.COMBO:
	case GuiCtrl.MULTIOPT:
	    if (m_Items != null) {
		if (updated) {
		    // Limpiar cualquier opción establecida previamente.
		    cleanNode();
		    
		    // Recuperar los items seleccionados (tienen id correlativo).
		    //#ifdef DEBUG_FRMNODE
		    //# System.out.println("\t(LIST, COMBO, MULTIOPT), Recuperar 1 ITEM");
		    //#endif
		    id = dlg.getInitId(Dialog.CTRL_MAIN);
		    while(id != GuiCtrl.INVALID_ITEM_ID) {
			//#ifdef DEBUG_FRMNODE
			//# System.out.println("\tItem seleccionado : " + id);
			//#endif
			item = selectItemById(id);
			item.m_Selected = true;
			//#ifdef DEBUG_FRMNODE
			//# System.out.println("\t(LIST, COMBO, MULTIOPT), Recuperar siguiente ITEM");
			//#endif
			id = dlg.getNextId(Dialog.CTRL_MAIN);
		    }
		}
		else {
		    // Inicializar el control principal del Dialogo.
		    e = m_Items.elements();
		    while(e.hasMoreElements()) {
			item = (FrmItem)e.nextElement();
			//#ifdef DEBUG_FRMNODE
			//#  System.out.println("\tAñadiendo item: " + item.m_Desc);
			//#endif
			dlg.addText(Dialog.CTRL_MAIN, item.m_Id, item.m_Desc );
		    }

		    // Don't worry be hacker!
		    putValue();

		    // Establecer los items seleccionados.
		    e = m_Items.elements();
		    while(e.hasMoreElements()) {
			item = (FrmItem)e.nextElement();
			if(item.m_Selected) {
			    //#ifdef DEBUG_FRMNODE
			    //#  System.out.println("\tSeleccionando item: " + item.m_Id);
			    //#endif
			    dlg.selectItem(Dialog.CTRL_MAIN, item.m_Id);
			}
		    }
		}
	    }
	    break;
	case GuiCtrl.TIME:
	case GuiCtrl.DATE:
	case GuiCtrl.DAY:
	    if (updated) {
		// Limpiar cualquier opción establecida previamente.
		cleanNode();
		m_Value.append(Integer.toHexString(dlg.getValue(Dialog.CTRL_MAIN)));
	    }
	    else {

		// Don't worry be hacker!
		putValue();

                if (m_Value.length() > 0) {
                    dlg.setValue(Dialog.CTRL_MAIN, Integer.parseInt(m_Value.toString(), 16));
                }
	    }
	    break;
	case GuiCtrl.IPADDR:
	case GuiCtrl.TEXT:
	case GuiCtrl.NUMSP:
	case GuiCtrl.NUMERIC:
	case GuiCtrl.PERCENT:
	case GuiCtrl.DECIMAL:
	case GuiCtrl.REAL:
	    if (updated) {
		// Limpiar cualquier opción establecida previamente.
		cleanNode();

		// Recuperar el contenido del control
		dlg.getText(Dialog.CTRL_MAIN, m_Value);
	    }
	    else {
		// Don't worry be hacker!
		putValue();
		
		// Establecer el contenido de texto.
		dlg.setText(Dialog.CTRL_MAIN, m_Value.toString());
	    }
	}

	//#ifdef DEBUG_FRMNODE
	//# System.out.println("\tContenido de nodo: " + m_Value.toString());
	//#endif

	//#ifdef DEBUG_FRMNODE
	//# System.out.println("FrmNode::updatedDialog() EXITING");
	//#endif

    }

    public final boolean calcEqual(FrmItem item) {
	boolean ret;
	int posp1;
	int posp2;
	String strvalue;

	// Si la opcion del item es comodin ...
	if (item.m_Desc.compareTo(FrmItem.JOCKER) == 0) {
	    // ... Eureka!!!
	    return true;
	}

	ret = false;
	switch (m_Type) {
	case GuiCtrl.LIST:
	case GuiCtrl.MULTIOPT:
	case GuiCtrl.COMBO:
	    // Evaluar si el item que se prueba esta seleccionado
	    ret = item.m_Selected;
	    break;
	case GuiCtrl.IPADDR:
	case GuiCtrl.TEXT:
	    ret = item.m_Desc.equalsIgnoreCase(m_Value.toString());
	    break;
	case GuiCtrl.NUMSP:
	case GuiCtrl.NUMERIC:
 	    ret = (Integer.parseInt(m_Value.toString()) == Integer.parseInt(item.m_Desc));
	    break;
	case GuiCtrl.TIME:
	case GuiCtrl.DATE:
	case GuiCtrl.DAY:
	    ret = (Integer.parseInt(m_Value.toString(), 16) == Integer.parseInt(item.m_Desc, 16));
	    break;
	case GuiCtrl.PERCENT:
	    // Basta que uno no tenga porcentaje para devolver false
	    strvalue = m_Value.toString();
	    posp1 = item.m_Desc.indexOf('%');
	    posp2 = strvalue.indexOf('%');
	    if ((posp1 > -1) || (posp2 > -1)) {
		if ((posp1 == -1) || (posp2 == -1)) {
		    ret = false;
		    break;
		}
	    }
	    ret = (Utils.memtod(strvalue) == Utils.memtod(item.m_Desc));
	    break;
	case GuiCtrl.DECIMAL:
	case GuiCtrl.REAL:
	    ret = (Utils.memtod(m_Value.toString()) == Utils.memtod(item.m_Desc));
	    break;
	}
	return ret;
    }

    public final boolean calcMajor(FrmItem item) {
	boolean ret;
	int posp1;
	int posp2;
	String strvalue;
	Enumeration e;
	FrmItem value;

	// Si la opcion del item es comodin ...
	if (item.m_Desc.compareTo(FrmItem.JOCKER) == 0) {
	    // ... Eureka!!!
	    return true;
	}

	ret = false;
	switch (m_Type) {
	case GuiCtrl.LIST:
	case GuiCtrl.MULTIOPT:
	case GuiCtrl.COMBO:
	    // Comprobar si alguno de los seleccionados es mayor que el Item que se prueba
	    e = m_Items.elements();
	    while(e.hasMoreElements()) {
		value = (FrmItem)e.nextElement();
		if(value.m_Selected) {
		    if ((Utils.memicmp(value.m_Desc, item.m_Desc) > 0)) {
			ret = true;
			break;
		    }
		}
	    }
	    break;
	case GuiCtrl.IPADDR:
	case GuiCtrl.TEXT:
	    ret = (Utils.memicmp(m_Value.toString(), item.m_Desc) > 0);
	    break;
	case GuiCtrl.NUMSP:
	case GuiCtrl.NUMERIC:
 	    ret = (Integer.parseInt(m_Value.toString()) > Integer.parseInt(item.m_Desc));
	    break;
	case GuiCtrl.TIME:
	case GuiCtrl.DATE:
	case GuiCtrl.DAY:
	    ret = (Integer.parseInt(m_Value.toString(), 16) > Integer.parseInt(item.m_Desc, 16));
	    break;
	case GuiCtrl.PERCENT:
	    // Basta que uno no tenga porcentaje para devolver false
	    strvalue = m_Value.toString();
	    posp1 = item.m_Desc.indexOf('%');
	    posp2 = strvalue.indexOf('%');
	    if ((posp1 > -1) || (posp2 > -1)) {
		if ((posp1 == -1) || (posp2 == -1)) {
		    ret = false;
		    break;
		}
	    }
	    ret = (Utils.memtod(strvalue) > Utils.memtod(item.m_Desc));
	    break;
	case GuiCtrl.DECIMAL:
	case GuiCtrl.REAL:
	    ret = (Utils.memtod(m_Value.toString()) > Utils.memtod(item.m_Desc));
	    break;
	}
	return ret;
    }

    public final boolean calcMinor(FrmItem item) {
	boolean ret;
	int posp1;
	int posp2;
	String strvalue;
	Enumeration e;
	FrmItem value;

	// Si la opcion del item es comodin ...
	if (item.m_Desc.compareTo(FrmItem.JOCKER) == 0) {
	    // ... Eureka!!!
	    return true;
	}

	ret = false;
	switch (m_Type) {
	case GuiCtrl.LIST:
	case GuiCtrl.MULTIOPT:
	case GuiCtrl.COMBO:
	    // Comprobar si alguno de los seleccionados es mayor que el Item que se prueba
	    e = m_Items.elements();
	    while(e.hasMoreElements()) {
		value = (FrmItem)e.nextElement();
		if(value.m_Selected) {
		    if ((Utils.memicmp(value.m_Desc, item.m_Desc) < 0)) {
			ret = true;
			break;
		    }
		}
	    }
	    break;
	case GuiCtrl.IPADDR:
	case GuiCtrl.TEXT:
	    ret = (Utils.memicmp(m_Value.toString(), item.m_Desc) < 0);
	    break;
	case GuiCtrl.NUMSP:
	case GuiCtrl.NUMERIC:
 	    ret = (Integer.parseInt(m_Value.toString()) < Integer.parseInt(item.m_Desc));
	    break;
	case GuiCtrl.TIME:
	case GuiCtrl.DATE:
	case GuiCtrl.DAY:
	    ret = (Integer.parseInt(m_Value.toString(), 16) < Integer.parseInt(item.m_Desc, 16));
	    break;
	case GuiCtrl.PERCENT:
	    // Basta que uno no tenga porcentaje para devolver false
	    strvalue = m_Value.toString();
	    posp1 = item.m_Desc.indexOf('%');
	    posp2 = strvalue.indexOf('%');
	    if ((posp1 > -1) || (posp2 > -1)) {
		if ((posp1 == -1) || (posp2 == -1)) {
		    ret = false;
		    break;
		}
	    }
	    ret = (Utils.memtod(strvalue) < Utils.memtod(item.m_Desc));
	    break;
	case GuiCtrl.DECIMAL:
	case GuiCtrl.REAL:
	    ret = (Utils.memtod(m_Value.toString()) < Utils.memtod(item.m_Desc));
	    break;
	}
	return ret;
    }

    public final boolean calcMajEq(FrmItem item) {
	return !calcMinor(item);
    }

    public final boolean calcMinEq(FrmItem item) {
	return !calcMajor(item);
    }
}
